# -*- coding: utf-8 -*-
"""
数据验证器模块

提供各种数据验证功能，包括：
- 基础数据类型验证
- 业务规则验证
- 格式验证
- 中国特色数据验证（身份证、手机号等）
"""

import re
from datetime import datetime, date
from typing import Any, Optional, List, Dict, Union, Callable
from decimal import Decimal
import phonenumbers
from email_validator import validate_email as email_validate, EmailNotValidError


class ValidationResult:
    """验证结果类"""
    
    def __init__(self, is_valid: bool, error_message: str = "", error_code: str = ""):
        self.is_valid = is_valid
        self.error_message = error_message
        self.error_code = error_code
    
    def __bool__(self) -> bool:
        return self.is_valid
    
    def __str__(self) -> str:
        return self.error_message if not self.is_valid else "验证通过"


class BaseValidator:
    """基础验证器"""
    
    @staticmethod
    def is_empty(value: Any) -> bool:
        """检查是否为空值"""
        if value is None:
            return True
        if isinstance(value, str):
            return value.strip() == ""
        if isinstance(value, (list, dict, tuple)):
            return len(value) == 0
        return False
    
    @staticmethod
    def is_required(value: Any) -> ValidationResult:
        """必填验证"""
        if BaseValidator.is_empty(value):
            return ValidationResult(False, "该字段为必填项", "REQUIRED")
        return ValidationResult(True)
    
    @staticmethod
    def min_length(value: str, min_len: int) -> ValidationResult:
        """最小长度验证"""
        if not isinstance(value, str):
            return ValidationResult(False, "值必须是字符串", "INVALID_TYPE")
        
        if len(value) < min_len:
            return ValidationResult(False, f"长度不能小于{min_len}个字符", "MIN_LENGTH")
        return ValidationResult(True)
    
    @staticmethod
    def max_length(value: str, max_len: int) -> ValidationResult:
        """最大长度验证"""
        if not isinstance(value, str):
            return ValidationResult(False, "值必须是字符串", "INVALID_TYPE")
        
        if len(value) > max_len:
            return ValidationResult(False, f"长度不能超过{max_len}个字符", "MAX_LENGTH")
        return ValidationResult(True)
    
    @staticmethod
    def length_range(value: str, min_len: int, max_len: int) -> ValidationResult:
        """长度范围验证"""
        min_result = BaseValidator.min_length(value, min_len)
        if not min_result.is_valid:
            return min_result
        
        max_result = BaseValidator.max_length(value, max_len)
        if not max_result.is_valid:
            return max_result
        
        return ValidationResult(True)
    
    @staticmethod
    def in_choices(value: Any, choices: List[Any]) -> ValidationResult:
        """选择项验证"""
        if value not in choices:
            return ValidationResult(False, f"值必须在{choices}中选择", "INVALID_CHOICE")
        return ValidationResult(True)
    
    @staticmethod
    def regex_match(value: str, pattern: str, error_message: str = "格式不正确") -> ValidationResult:
        """正则表达式验证"""
        if not isinstance(value, str):
            return ValidationResult(False, "值必须是字符串", "INVALID_TYPE")
        
        if not re.match(pattern, value):
            return ValidationResult(False, error_message, "REGEX_MISMATCH")
        return ValidationResult(True)


class NumberValidator:
    """数字验证器"""
    
    @staticmethod
    def is_number(value: Any) -> ValidationResult:
        """数字验证"""
        if not isinstance(value, (int, float, Decimal)):
            try:
                float(value)
            except (TypeError, ValueError):
                return ValidationResult(False, "值必须是数字", "INVALID_NUMBER")
        return ValidationResult(True)
    
    @staticmethod
    def is_integer(value: Any) -> ValidationResult:
        """整数验证"""
        if not isinstance(value, int):
            try:
                int(value)
            except (TypeError, ValueError):
                return ValidationResult(False, "值必须是整数", "INVALID_INTEGER")
        return ValidationResult(True)
    
    @staticmethod
    def is_positive(value: Union[int, float, Decimal]) -> ValidationResult:
        """正数验证"""
        number_result = NumberValidator.is_number(value)
        if not number_result.is_valid:
            return number_result
        
        if float(value) <= 0:
            return ValidationResult(False, "值必须是正数", "NOT_POSITIVE")
        return ValidationResult(True)
    
    @staticmethod
    def is_non_negative(value: Union[int, float, Decimal]) -> ValidationResult:
        """非负数验证"""
        number_result = NumberValidator.is_number(value)
        if not number_result.is_valid:
            return number_result
        
        if float(value) < 0:
            return ValidationResult(False, "值不能为负数", "NEGATIVE_NUMBER")
        return ValidationResult(True)
    
    @staticmethod
    def min_value(value: Union[int, float, Decimal], min_val: Union[int, float, Decimal]) -> ValidationResult:
        """最小值验证"""
        number_result = NumberValidator.is_number(value)
        if not number_result.is_valid:
            return number_result
        
        if float(value) < float(min_val):
            return ValidationResult(False, f"值不能小于{min_val}", "MIN_VALUE")
        return ValidationResult(True)
    
    @staticmethod
    def max_value(value: Union[int, float, Decimal], max_val: Union[int, float, Decimal]) -> ValidationResult:
        """最大值验证"""
        number_result = NumberValidator.is_number(value)
        if not number_result.is_valid:
            return number_result
        
        if float(value) > float(max_val):
            return ValidationResult(False, f"值不能大于{max_val}", "MAX_VALUE")
        return ValidationResult(True)
    
    @staticmethod
    def value_range(value: Union[int, float, Decimal], min_val: Union[int, float, Decimal], max_val: Union[int, float, Decimal]) -> ValidationResult:
        """数值范围验证"""
        min_result = NumberValidator.min_value(value, min_val)
        if not min_result.is_valid:
            return min_result
        
        max_result = NumberValidator.max_value(value, max_val)
        if not max_result.is_valid:
            return max_result
        
        return ValidationResult(True)


class DateValidator:
    """日期验证器"""
    
    @staticmethod
    def is_valid_date(value: Any) -> ValidationResult:
        """日期有效性验证"""
        if isinstance(value, (datetime, date)):
            return ValidationResult(True)
        
        if isinstance(value, str):
            try:
                datetime.strptime(value, "%Y-%m-%d")
                return ValidationResult(True)
            except ValueError:
                try:
                    datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
                    return ValidationResult(True)
                except ValueError:
                    return ValidationResult(False, "日期格式不正确", "INVALID_DATE")
        
        return ValidationResult(False, "值必须是有效日期", "INVALID_DATE")
    
    @staticmethod
    def is_past_date(value: Union[datetime, date, str]) -> ValidationResult:
        """过去日期验证"""
        date_result = DateValidator.is_valid_date(value)
        if not date_result.is_valid:
            return date_result
        
        if isinstance(value, str):
            try:
                value = datetime.strptime(value, "%Y-%m-%d").date()
            except ValueError:
                value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S").date()
        elif isinstance(value, datetime):
            value = value.date()
        
        if value >= datetime.now().date():
            return ValidationResult(False, "日期必须是过去的日期", "NOT_PAST_DATE")
        return ValidationResult(True)
    
    @staticmethod
    def is_future_date(value: Union[datetime, date, str]) -> ValidationResult:
        """未来日期验证"""
        date_result = DateValidator.is_valid_date(value)
        if not date_result.is_valid:
            return date_result
        
        if isinstance(value, str):
            try:
                value = datetime.strptime(value, "%Y-%m-%d").date()
            except ValueError:
                value = datetime.strptime(value, "%Y-%m-%d %H:%M:%S").date()
        elif isinstance(value, datetime):
            value = value.date()
        
        if value <= datetime.now().date():
            return ValidationResult(False, "日期必须是未来的日期", "NOT_FUTURE_DATE")
        return ValidationResult(True)
    
    @staticmethod
    def age_range(birth_date: Union[datetime, date, str], min_age: int, max_age: int) -> ValidationResult:
        """年龄范围验证"""
        date_result = DateValidator.is_valid_date(birth_date)
        if not date_result.is_valid:
            return date_result
        
        if isinstance(birth_date, str):
            try:
                birth_date = datetime.strptime(birth_date, "%Y-%m-%d").date()
            except ValueError:
                birth_date = datetime.strptime(birth_date, "%Y-%m-%d %H:%M:%S").date()
        elif isinstance(birth_date, datetime):
            birth_date = birth_date.date()
        
        today = datetime.now().date()
        age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
        
        if age < min_age or age > max_age:
            return ValidationResult(False, f"年龄必须在{min_age}-{max_age}岁之间", "AGE_OUT_OF_RANGE")
        return ValidationResult(True)


class ChineseValidator:
    """中国特色数据验证器"""
    
    @staticmethod
    def validate_id_card(id_card: str) -> ValidationResult:
        """身份证号验证"""
        if not isinstance(id_card, str):
            return ValidationResult(False, "身份证号必须是字符串", "INVALID_TYPE")
        
        # 去除空格
        id_card = id_card.strip()
        
        # 长度检查
        if len(id_card) != 18:
            return ValidationResult(False, "身份证号必须是18位", "INVALID_LENGTH")
        
        # 格式检查
        if not re.match(r'^[0-9]{17}[0-9Xx]$', id_card):
            return ValidationResult(False, "身份证号格式不正确", "INVALID_FORMAT")
        
        # 地区码检查
        area_code = id_card[:6]
        if not ChineseValidator._is_valid_area_code(area_code):
            return ValidationResult(False, "身份证号地区码无效", "INVALID_AREA_CODE")
        
        # 出生日期检查
        birth_date = id_card[6:14]
        if not ChineseValidator._is_valid_birth_date(birth_date):
            return ValidationResult(False, "身份证号出生日期无效", "INVALID_BIRTH_DATE")
        
        # 校验码检查
        if not ChineseValidator._is_valid_check_code(id_card):
            return ValidationResult(False, "身份证号校验码无效", "INVALID_CHECK_CODE")
        
        return ValidationResult(True)
    
    @staticmethod
    def _is_valid_area_code(area_code: str) -> bool:
        """验证地区码"""
        # 简化版本，实际应用中应该有完整的地区码表
        return area_code[:2] in ['11', '12', '13', '14', '15', '21', '22', '23', '31', '32', '33', '34', '35', '36', '37', '41', '42', '43', '44', '45', '46', '50', '51', '52', '53', '54', '61', '62', '63', '64', '65', '71', '81', '82']
    
    @staticmethod
    def _is_valid_birth_date(birth_date: str) -> bool:
        """验证出生日期"""
        try:
            year = int(birth_date[:4])
            month = int(birth_date[4:6])
            day = int(birth_date[6:8])
            
            # 基本范围检查
            if year < 1900 or year > datetime.now().year:
                return False
            if month < 1 or month > 12:
                return False
            if day < 1 or day > 31:
                return False
            
            # 创建日期对象进行验证
            datetime(year, month, day)
            return True
        except (ValueError, TypeError):
            return False
    
    @staticmethod
    def _is_valid_check_code(id_card: str) -> bool:
        """验证校验码"""
        # 加权因子
        weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
        # 校验码对应表
        check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
        
        # 计算校验和
        total = sum(int(id_card[i]) * weights[i] for i in range(17))
        check_index = total % 11
        
        return id_card[17].upper() == check_codes[check_index]
    
    @staticmethod
    def get_info_from_id_card(id_card: str) -> Dict[str, Any]:
        """从身份证号提取信息"""
        result = ChineseValidator.validate_id_card(id_card)
        if not result.is_valid:
            return {"error": result.error_message}
        
        birth_date = id_card[6:14]
        year = int(birth_date[:4])
        month = int(birth_date[4:6])
        day = int(birth_date[6:8])
        
        birth_datetime = datetime(year, month, day)
        age = datetime.now().year - year - ((datetime.now().month, datetime.now().day) < (month, day))
        
        # 性别：倒数第二位，奇数为男，偶数为女
        gender = "男" if int(id_card[16]) % 2 == 1 else "女"
        
        return {
            "birth_date": birth_datetime.date(),
            "age": age,
            "gender": gender,
            "area_code": id_card[:6]
        }
    
    @staticmethod
    def validate_phone(phone: str) -> ValidationResult:
        """手机号验证"""
        if not isinstance(phone, str):
            return ValidationResult(False, "手机号必须是字符串", "INVALID_TYPE")
        
        # 去除空格和特殊字符
        phone = re.sub(r'[\s\-\(\)]', '', phone)
        
        # 中国大陆手机号格式：1开头，第二位是3-9，总共11位
        if not re.match(r'^1[3-9]\d{9}$', phone):
            return ValidationResult(False, "手机号格式不正确", "INVALID_PHONE_FORMAT")
        
        return ValidationResult(True)
    
    @staticmethod
    def validate_landline(landline: str) -> ValidationResult:
        """固定电话验证"""
        if not isinstance(landline, str):
            return ValidationResult(False, "固定电话必须是字符串", "INVALID_TYPE")
        
        # 去除空格和特殊字符
        landline = re.sub(r'[\s\-\(\)]', '', landline)
        
        # 固定电话格式：区号(2-4位) + 号码(7-8位)
        if not re.match(r'^0\d{2,3}\d{7,8}$', landline):
            return ValidationResult(False, "固定电话格式不正确", "INVALID_LANDLINE_FORMAT")
        
        return ValidationResult(True)
    
    @staticmethod
    def validate_postal_code(postal_code: str) -> ValidationResult:
        """邮政编码验证"""
        if not isinstance(postal_code, str):
            return ValidationResult(False, "邮政编码必须是字符串", "INVALID_TYPE")
        
        # 去除空格
        postal_code = postal_code.strip()
        
        # 中国邮政编码：6位数字
        if not re.match(r'^\d{6}$', postal_code):
            return ValidationResult(False, "邮政编码格式不正确", "INVALID_POSTAL_CODE")
        
        return ValidationResult(True)


class EmailValidator:
    """邮箱验证器"""
    
    @staticmethod
    def validate_email(email: str) -> ValidationResult:
        """邮箱验证"""
        if not isinstance(email, str):
            return ValidationResult(False, "邮箱必须是字符串", "INVALID_TYPE")
        
        try:
            # 使用email-validator库进行验证
            valid_email = email_validate(email)
            return ValidationResult(True)
        except EmailNotValidError as e:
            return ValidationResult(False, f"邮箱格式不正确: {str(e)}", "INVALID_EMAIL")
    
    @staticmethod
    def validate_email_domain(email: str, allowed_domains: List[str]) -> ValidationResult:
        """邮箱域名验证"""
        email_result = EmailValidator.validate_email(email)
        if not email_result.is_valid:
            return email_result
        
        domain = email.split('@')[1].lower()
        if domain not in [d.lower() for d in allowed_domains]:
            return ValidationResult(False, f"邮箱域名不在允许列表中: {allowed_domains}", "INVALID_DOMAIN")
        
        return ValidationResult(True)


class BusinessValidator:
    """业务验证器"""
    
    @staticmethod
    def validate_customer_age(birth_date: Union[datetime, date, str]) -> ValidationResult:
        """客户年龄验证（中老年旅游）"""
        return DateValidator.age_range(birth_date, 45, 100)
    
    @staticmethod
    def validate_price(price: Union[int, float, Decimal]) -> ValidationResult:
        """价格验证"""
        number_result = NumberValidator.is_number(price)
        if not number_result.is_valid:
            return number_result
        
        # 价格必须为正数
        if float(price) <= 0:
            return ValidationResult(False, "价格必须大于0", "INVALID_PRICE")
        
        # 价格不能超过100万
        if float(price) > 1000000:
            return ValidationResult(False, "价格不能超过100万", "PRICE_TOO_HIGH")
        
        return ValidationResult(True)
    
    @staticmethod
    def validate_tour_duration(duration: int) -> ValidationResult:
        """旅游天数验证"""
        integer_result = NumberValidator.is_integer(duration)
        if not integer_result.is_valid:
            return integer_result
        
        if duration < 1:
            return ValidationResult(False, "旅游天数必须至少1天", "INVALID_DURATION")
        
        if duration > 365:
            return ValidationResult(False, "旅游天数不能超过365天", "DURATION_TOO_LONG")
        
        return ValidationResult(True)
    
    @staticmethod
    def validate_participant_count(count: int, min_count: int, max_count: int) -> ValidationResult:
        """参与人数验证"""
        integer_result = NumberValidator.is_integer(count)
        if not integer_result.is_valid:
            return integer_result
        
        return NumberValidator.value_range(count, min_count, max_count)
    
    @staticmethod
    def validate_probability(probability: Union[int, float]) -> ValidationResult:
        """概率验证（0-100）"""
        number_result = NumberValidator.is_number(probability)
        if not number_result.is_valid:
            return number_result
        
        return NumberValidator.value_range(probability, 0, 100)


class CompositeValidator:
    """复合验证器"""
    
    def __init__(self):
        self.validators: List[Callable[[Any], ValidationResult]] = []
    
    def add_validator(self, validator: Callable[[Any], ValidationResult]) -> 'CompositeValidator':
        """添加验证器"""
        self.validators.append(validator)
        return self
    
    def validate(self, value: Any) -> ValidationResult:
        """执行所有验证"""
        for validator in self.validators:
            result = validator(value)
            if not result.is_valid:
                return result
        return ValidationResult(True)


# 便捷验证函数
def validate_id_card(id_card: str) -> bool:
    """验证身份证号"""
    return ChineseValidator.validate_id_card(id_card).is_valid


def validate_phone(phone: str) -> bool:
    """验证手机号"""
    return ChineseValidator.validate_phone(phone).is_valid


def validate_email(email: str) -> bool:
    """验证邮箱"""
    return EmailValidator.validate_email(email).is_valid


def validate_required(value: Any) -> bool:
    """验证必填项"""
    return BaseValidator.is_required(value).is_valid


def validate_positive_number(value: Union[int, float, Decimal]) -> bool:
    """验证正数"""
    return NumberValidator.is_positive(value).is_valid


def create_customer_validator() -> CompositeValidator:
    """创建客户验证器"""
    return CompositeValidator()


def create_phone_validator() -> CompositeValidator:
    """创建手机号验证器"""
    return (CompositeValidator()
            .add_validator(BaseValidator.is_required)
            .add_validator(ChineseValidator.validate_phone))


def create_email_validator() -> CompositeValidator:
    """创建邮箱验证器"""
    return (CompositeValidator()
            .add_validator(EmailValidator.validate_email))


def create_id_card_validator() -> CompositeValidator:
    """创建身份证验证器"""
    return (CompositeValidator()
            .add_validator(BaseValidator.is_required)
            .add_validator(ChineseValidator.validate_id_card))


def create_price_validator() -> CompositeValidator:
    """创建价格验证器"""
    return (CompositeValidator()
            .add_validator(BaseValidator.is_required)
            .add_validator(BusinessValidator.validate_price))


# 验证装饰器
def validate_input(**validators):
    """输入验证装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for param_name, validator in validators.items():
                if param_name in kwargs:
                    result = validator(kwargs[param_name])
                    if not result.is_valid:
                        from .exceptions import ValidationError
                        raise ValidationError(result.error_message, param_name, kwargs[param_name])
            return func(*args, **kwargs)
        return wrapper
    return decorator